/* Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.dromara.workflow.flowable;

import org.flowable.bpmn.model.Event;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.image.ProcessDiagramGenerator;

import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.*;


/**
 * @Author 刘武贵
 * @Date 2024/8/15 22:00
 * @Description 功能描述：提供了生成流程图的默认实现，可用于可视化流程定义
 * Class to generate an image based the diagram interchange information in a BPMN 2.0 process.
 */
/**
 * 自定义默认流程图生成器类
 * 该类实现了ProcessDiagramGenerator接口，用于生成流程图的图像
 * 它提供了一种方式来可视化流程定义，使得流程的结构和流向更加直观易懂
 */
public class CustomDefaultProcessDiagramGenerator implements ProcessDiagramGenerator {

    /**
     * 存储活动绘制指令的映射，键为BaseElement的子类类型，值为活动绘制指令
     */
    protected Map<Class<? extends BaseElement>, ActivityDrawInstruction> activityDrawInstructions = new HashMap<>();

    /**
     * 存储工件绘制指令的映射，键为BaseElement的子类类型，值为工件绘制指令
     */
    protected Map<Class<? extends BaseElement>, ArtifactDrawInstruction> artifactDrawInstructions = new HashMap<>();


    /**
     * 默认构造方法
     * 此构造方法初始化了CustomDefaultProcessDiagramGenerator的实例，使用了默认的缩放比例1.0
     */
    public CustomDefaultProcessDiagramGenerator() {
        this(1.0);
    }

    // The instructions on how to draw a certain construct is
    // created statically and stored in a map for performance.
    // 自定义默认流程图生成器构造函数
    public CustomDefaultProcessDiagramGenerator(final double scaleFactor) {
        // start event
        // 初始化开始事件的绘制指令
        activityDrawInstructions.put(StartEvent.class, new ActivityDrawInstruction() {

            /**
             * 绘制开始事件
             * @param processDiagramCanvas 流程图绘制画布
             * @param bpmnModel BPMN模型，用于获取流程节点的图形信息
             * @param flowNode 流程节点，此处特指开始事件节点
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取流程节点的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 将流程节点转换为开始事件
                StartEvent startEvent = (StartEvent) flowNode;
                // 检查开始事件是否有定义的事件
                if (startEvent.getEventDefinitions() != null && !startEvent.getEventDefinitions().isEmpty()) {
                    // 获取第一个事件定义
                    EventDefinition eventDefinition = startEvent.getEventDefinitions().get(0);
                    // 根据事件类型绘制不同的开始事件
                    if (eventDefinition instanceof TimerEventDefinition) {
                        processDiagramCanvas.drawTimerStartEvent(graphicInfo, scaleFactor);
                    } else if (eventDefinition instanceof ErrorEventDefinition) {
                        processDiagramCanvas.drawErrorStartEvent(graphicInfo, scaleFactor);
                    } else if (eventDefinition instanceof EscalationEventDefinition) {
                        processDiagramCanvas.drawEscalationStartEvent(graphicInfo, scaleFactor);
                    } else if (eventDefinition instanceof ConditionalEventDefinition) {
                        processDiagramCanvas.drawConditionalStartEvent(graphicInfo, scaleFactor);
                    } else if (eventDefinition instanceof SignalEventDefinition) {
                        processDiagramCanvas.drawSignalStartEvent(graphicInfo, scaleFactor);
                    } else if (eventDefinition instanceof MessageEventDefinition) {
                        processDiagramCanvas.drawMessageStartEvent(graphicInfo, scaleFactor);
                    } else {
                        // 如果事件类型不匹配，则绘制默认开始事件
                        processDiagramCanvas.drawNoneStartEvent(graphicInfo);
                    }
                } else {
                    // 如果开始事件没有事件定义，则检查扩展元素中的事件类型
                    List<ExtensionElement> eventTypeElements = startEvent.getExtensionElements().get("eventType");
                    if (eventTypeElements != null && eventTypeElements.size() > 0) {
                        // 绘制事件注册开始事件
                        processDiagramCanvas.drawEventRegistryStartEvent(graphicInfo, scaleFactor);
                    } else {
                        // 绘制默认开始事件
                        processDiagramCanvas.drawNoneStartEvent(graphicInfo);
                    }
                }
            }
        });

        // signal catch
        // 将中间捕获事件的绘制指令添加到活动绘制指令映射中
        activityDrawInstructions.put(IntermediateCatchEvent.class, new ActivityDrawInstruction() {

            /**
             * 绘制中间捕获事件的图形表示
             * @param processDiagramCanvas 自定义的流程图画布，用于实际绘制事件
             * @param bpmnModel BPMN模型，包含流程的所有信息和图形数据
             * @param flowNode 流节点，此处特指中间捕获事件
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取节点的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 将流节点转换为中间捕获事件
                IntermediateCatchEvent intermediateCatchEvent = (IntermediateCatchEvent) flowNode;
                // 检查中间捕获事件的事件定义是否非空
                if (intermediateCatchEvent.getEventDefinitions() != null && !intermediateCatchEvent.getEventDefinitions().isEmpty()) {
                    // 根据不同的事件类型绘制相应的中间捕获事件
                    if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof SignalEventDefinition) {
                        // 绘制信号事件
                        processDiagramCanvas.drawCatchingSignalEvent(flowNode.getName(), graphicInfo, true, scaleFactor);
                    } else if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof TimerEventDefinition) {
                        // 绘制定时器事件
                        processDiagramCanvas.drawCatchingTimerEvent(flowNode.getName(), graphicInfo, true, scaleFactor);
                    } else if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof MessageEventDefinition) {
                        // 绘制消息事件
                        processDiagramCanvas.drawCatchingMessageEvent(flowNode.getName(), graphicInfo, true, scaleFactor);
                    } else if (intermediateCatchEvent.getEventDefinitions().get(0) instanceof ConditionalEventDefinition) {
                        // 绘制条件事件
                        processDiagramCanvas.drawCatchingConditionalEvent(flowNode.getName(), graphicInfo, true, scaleFactor);
                    }
                }
            }
        });

        // signal throw
        // 将抛出事件的绘制指令添加到活动绘制指令映射中
        activityDrawInstructions.put(ThrowEvent.class, new ActivityDrawInstruction() {

            /**
             * 根据不同的事件定义绘制抛出事件
             *
             * @param processDiagramCanvas 用于绘制流程图的画布
             * @param bpmnModel BPMN模型，包含流程的所有信息
             * @param flowNode 需要绘制的抛出事件节点
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取节点的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 将节点转换为抛出事件
                ThrowEvent throwEvent = (ThrowEvent) flowNode;
                // 根据事件定义的类型绘制不同的抛出事件
                if (throwEvent.getEventDefinitions() != null && !throwEvent.getEventDefinitions().isEmpty()) {
                    // 判断并绘制信号事件
                    if (throwEvent.getEventDefinitions().get(0) instanceof SignalEventDefinition) {
                        processDiagramCanvas.drawThrowingSignalEvent(graphicInfo, scaleFactor);
                        // 判断并绘制升级事件
                    } else if (throwEvent.getEventDefinitions().get(0) instanceof EscalationEventDefinition) {
                        processDiagramCanvas.drawThrowingEscalationEvent(graphicInfo, scaleFactor);
                        // 判断并绘制补偿事件
                    } else if (throwEvent.getEventDefinitions().get(0) instanceof CompensateEventDefinition) {
                        processDiagramCanvas.drawThrowingCompensateEvent(graphicInfo, scaleFactor);
                        // 默认情况下绘制无事件
                    } else {
                        processDiagramCanvas.drawThrowingNoneEvent(graphicInfo, scaleFactor);
                    }
                } else {
                    // 如果没有事件定义，则默认绘制无事件
                    processDiagramCanvas.drawThrowingNoneEvent(graphicInfo, scaleFactor);
                }
            }
        });

        // end event
        // 将结束事件的绘制指令添加到活动绘制指令映射中
        activityDrawInstructions.put(EndEvent.class, new ActivityDrawInstruction() {

            /**
             * 根据给定的流程图信息和流对象绘制结束事件
             * 此方法专门用于处理结束事件的绘制，包括没有结束事件定义的情况，
             * 以及特定的结束事件定义如错误事件和升级事件
             *
             * @param processDiagramCanvas 自定义的流程图画布，用于实际绘制操作
             * @param bpmnModel BPMN模型，包含流程图的所有信息
             * @param flowNode 流节点，此处特指结束事件
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取流节点的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 将流节点强制转换为结束事件
                EndEvent endEvent = (EndEvent) flowNode;
                // 检查结束事件是否具有事件定义
                if (endEvent.getEventDefinitions() != null && !endEvent.getEventDefinitions().isEmpty()) {
                    // 根据第一个事件定义的类型绘制相应的结束事件
                    if (endEvent.getEventDefinitions().get(0) instanceof ErrorEventDefinition) {
                        processDiagramCanvas.drawErrorEndEvent(flowNode.getName(), graphicInfo, scaleFactor);
                    } else if (endEvent.getEventDefinitions().get(0) instanceof EscalationEventDefinition) {
                        processDiagramCanvas.drawEscalationEndEvent(flowNode.getName(), graphicInfo, scaleFactor);
                    } else {
                        // 如果事件定义不是错误或升级类型，则绘制普通结束事件
                        processDiagramCanvas.drawNoneEndEvent(graphicInfo, scaleFactor);
                    }
                } else {
                    // 如果没有事件定义，则同样绘制普通结束事件
                    processDiagramCanvas.drawNoneEndEvent(graphicInfo, scaleFactor);
                }
            }
        });

        // task
        // 将任务节点的绘制指令添加到活动绘制指令集合中
        activityDrawInstructions.put(Task.class, new ActivityDrawInstruction() {

            /**
             * 绘制任务节点
             * @param processDiagramCanvas 自定义的流程图画布，用于实际绘制节点
             * @param bpmnModel BPMN模型，包含流程节点的信息
             * @param flowNode 流节点，当前需要绘制的任务节点
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取节点的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 使用节点名称和图形信息，在画布上绘制任务节点
                processDiagramCanvas.drawTask(flowNode.getName(), graphicInfo, scaleFactor);
            }
        });

        // user task
        // 将用户任务的绘制指令添加到活动绘制指令映射中
        activityDrawInstructions.put(UserTask.class, new ActivityDrawInstruction() {

            /**
             * 绘制用户任务的图形表示
             * @param processDiagramCanvas 过程图绘制画布，用于实际的图形绘制操作
             * @param bpmnModel BPMN模型，包含流程节点信息和图形信息
             * @param flowNode 需要绘制的流程节点
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取流程节点对应的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 使用画布绘制用户任务，包括名称和图形信息
                processDiagramCanvas.drawUserTask(flowNode.getName(), graphicInfo, scaleFactor);
            }
        });

        // script task
        // 为脚本任务（ScriptTask）类型的任务定义绘制指令
        activityDrawInstructions.put(ScriptTask.class, new ActivityDrawInstruction() {

            /**
             * 绘制脚本任务的图形表示
             * @param processDiagramCanvas 绘图画布，用于绘制流程图元素
             * @param bpmnModel BPMN模型，包含流程的所有信息
             * @param flowNode 流节点，表示流程中的一个活动或网关
             * 此方法使用BPMN模型中的图形信息来绘制特定类型的流程节点
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取节点的图形信息，包括位置和尺寸
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 使用画布绘制脚本任务，传入节点名称、图形信息和缩放因子
                processDiagramCanvas.drawScriptTask(flowNode.getName(), graphicInfo, scaleFactor);
            }
        });

        // service task
        // 将服务任务的绘制指令添加到活动绘制指令映射中
        activityDrawInstructions.put(ServiceTask.class, new ActivityDrawInstruction() {

            /**
             * 根据服务任务的类型绘制不同的图标
             * 此方法解释了如何基于服务任务的类型绘制不同的图形表示
             *
             * @param processDiagramCanvas 用于绘制流程图的画布
             * @param bpmnModel BPMN模型，包含流程的所有信息
             * @param flowNode 要绘制的服务任务节点
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取节点的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 将流节点强制转换为服务任务
                ServiceTask serviceTask = (ServiceTask) flowNode;
                // 根据服务任务的类型绘制相应的图标
                if ("camel".equalsIgnoreCase(serviceTask.getType())) {
                    processDiagramCanvas.drawCamelTask(serviceTask.getName(), graphicInfo, scaleFactor);
                }else if (ServiceTask.HTTP_TASK.equalsIgnoreCase(serviceTask.getType())) {
                    processDiagramCanvas.drawHttpTask(serviceTask.getName(), graphicInfo, scaleFactor);
                } else if (ServiceTask.DMN_TASK.equalsIgnoreCase(serviceTask.getType())) {
                    processDiagramCanvas.drawDMNTask(serviceTask.getName(), graphicInfo, scaleFactor);
                } else if (ServiceTask.SHELL_TASK.equalsIgnoreCase(serviceTask.getType())) {
                    processDiagramCanvas.drawShellTask(serviceTask.getName(), graphicInfo, scaleFactor);
                } else {
                    // 对于其他类型的服务任务，使用默认的绘制方法
                    processDiagramCanvas.drawServiceTask(serviceTask.getName(), graphicInfo, scaleFactor);
                }
            }
        });

        // http service task
        // 将HttpServiceTask类的绘制指令添加到活动绘制指令映射中
        activityDrawInstructions.put(HttpServiceTask.class, new ActivityDrawInstruction() {

            /**
             * 绘制HttpServiceTask的图形表示
             * @param processDiagramCanvas 自定义的流程图画布，用于绘制流程图元素
             * @param bpmnModel BPMN模型，包含流程节点的详细信息
             * @param flowNode 流程节点，此处特指HttpServiceTask类的实例节点
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取流程节点的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 在画布上绘制HTTP任务图形，包括名称、图形信息和缩放比例
                processDiagramCanvas.drawHttpTask(flowNode.getName(), graphicInfo, scaleFactor);
            }
        });

        // receive task
        // 将接收任务的绘制指令添加到活动绘制指令映射中
        activityDrawInstructions.put(ReceiveTask.class, new ActivityDrawInstruction() {

            /**
             * 绘制接收任务的图形表示
             * @param processDiagramCanvas 用于绘制流程图的画布对象
             * @param bpmnModel BPMN模型，包含流程节点的详细信息
             * @param flowNode 需要绘制的流程节点，这里是接收任务
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取流程节点的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 在画布上绘制接收任务
                processDiagramCanvas.drawReceiveTask(flowNode.getName(), graphicInfo, scaleFactor);
            }
        });

        // send task
        // 将发送任务的绘制指令添加到活动绘制指令映射中
        activityDrawInstructions.put(SendTask.class, new ActivityDrawInstruction() {

            /**
             * 绘制发送任务图形
             * 此方法重写了ActivityDrawInstruction接口的draw方法，用于在流程图画布上绘制特定的发送任务组件
             *
             * @param processDiagramCanvas 自定义的流程图画布，用于绘制流程图元素
             * @param bpmnModel BPMN模型，包含了流程图的所有信息
             * @param flowNode 流节点，代表流程图中的一个元素
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取节点的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 使用流程图画布的绘制方法绘制发送任务
                processDiagramCanvas.drawSendTask(flowNode.getName(), graphicInfo, scaleFactor);
            }
        });

        // manual task
        // 将人工任务的绘制指令添加到活动绘制指令映射中
        activityDrawInstructions.put(ManualTask.class, new ActivityDrawInstruction() {

            /**
             * 绘制人工任务的图形表示
             *
             * @param processDiagramCanvas 用于绘制流程图的自定义画布
             * @param bpmnModel BPMN模型，包含流程节点的详细信息
             * @param flowNode 需要绘制的人工任务节点
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取节点的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 使用节点名称和图形信息绘制人工任务
                processDiagramCanvas.drawManualTask(flowNode.getName(), graphicInfo, scaleFactor);
            }
        });

        // send event service task
        // 将SendEventServiceTask类的绘图指令添加到活动绘图指令集合中
        activityDrawInstructions.put(SendEventServiceTask.class, new ActivityDrawInstruction() {

            /**
             * 绘制SendEventServiceTask类的图形表示
             * @param processDiagramCanvas 自定义的流程图画布，用于绘制流程图元素
             * @param bpmnModel BPMN模型，包含流程节点的详细信息
             * @param flowNode 流节点，代表流程中的一个活动或事件
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取流节点的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 在画布上绘制发送事件服务任务
                processDiagramCanvas.drawSendEventServiceTask(flowNode.getName(), graphicInfo, scaleFactor);
            }
        });

        // external worker service task
        // 将ExternalWorkerServiceTask类的绘图指令添加到活动绘图指令集合中
        activityDrawInstructions.put(ExternalWorkerServiceTask.class, new ActivityDrawInstruction() {

            /**
             * 绘制外部工作者服务任务的图形表示
             * @param processDiagramCanvas 自定义的流程图画布，用于绘制流程图元素
             * @param bpmnModel BPMN模型，包含流程节点的图形信息
             * @param flowNode 流程节点，具体为服务任务
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取流程节点的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 将流程节点转换为服务任务
                ServiceTask serviceTask = (ServiceTask) flowNode;
                // 在画布上绘制服务任务
                processDiagramCanvas.drawServiceTask(serviceTask.getName(), graphicInfo, scaleFactor);
            }
        });

        // case service task
        // 将服务任务的绘制指令添加到活动绘制指令集合中
        activityDrawInstructions.put(CaseServiceTask.class, new ActivityDrawInstruction() {

            /**
             * 绘制服务任务的图形表示
             * @param processDiagramCanvas 自定义的流程图画布，用于实际绘制元素
             * @param bpmnModel BPMN模型，包含流程节点的详细信息
             * @param flowNode 要绘制的流节点，这里是服务任务
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取节点的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 使用节点名称和图形信息绘制服务任务
                processDiagramCanvas.drawCaseServiceTask(flowNode.getName(), graphicInfo, scaleFactor);
            }
        });

        // businessRuleTask task
        // 将业务规则任务的绘制指令添加到活动绘制指令集合中
        activityDrawInstructions.put(BusinessRuleTask.class, new ActivityDrawInstruction() {

            /**
             * 绘制业务规则任务图形
             * 此方法解释如何在流程图中绘制业务规则任务节点
             *
             * @param processDiagramCanvas 用于绘制流程图的画布对象
             * @param bpmnModel BPMN模型对象，包含流程的所有信息
             * @param flowNode 流节点对象，代表流程中的一个节点
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取节点的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 使用节点名称和图形信息绘制业务规则任务
                processDiagramCanvas.drawBusinessRuleTask(flowNode.getName(), graphicInfo, scaleFactor);
            }
        });

        // exclusive gateway
        // 将独占网关(Exclusive Gateway)的绘制指令添加到活动绘制指令集中
        activityDrawInstructions.put(ExclusiveGateway.class, new ActivityDrawInstruction() {

            /**
             * 绘制独占网关
             *
             * @param processDiagramCanvas 自定义的流程图画布，用于绘制流程图元素
             * @param bpmnModel BPMN模型，包含流程节点的图形信息和业务逻辑
             * @param flowNode 流节点，此处特指独占网关节点
             * @throws IOException 如果在绘制过程中发生IO错误
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取节点的图形信息，scaleFactor为缩放比例
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 使用画布绘制独占网关
                processDiagramCanvas.drawExclusiveGateway(graphicInfo, scaleFactor);
            }
        });

        // inclusive gateway
        // 将包容网关的绘制指令添加到活动绘制指令映射中
        activityDrawInstructions.put(InclusiveGateway.class, new ActivityDrawInstruction() {

            /**
             * 绘制包容网关及其相关信息
             * @param processDiagramCanvas 自定义的流程图画布，用于绘制流程图元素
             * @param bpmnModel BPMN模型，包含流程节点的详细信息
             * @param flowNode 流节点，此处特指包容网关节点
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取包容网关的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 在画布上绘制包容网关
                processDiagramCanvas.drawInclusiveGateway(graphicInfo, scaleFactor);
            }
        });

        // parallel gateway
        // 将ParallelGateway类的绘制指令添加到活动绘制指令映射中
        activityDrawInstructions.put(ParallelGateway.class, new ActivityDrawInstruction() {

            /**
             * 绘制平行网关节点
             *
             * @param processDiagramCanvas 流程图画布，用于绘制流程图元素
             * @param bpmnModel BPMN模型，包含流程节点和它们的图形信息
             * @param flowNode 流节点，此处特指ParallelGateway类型的节点
             * 该方法根据BPMN模型中流节点的图形信息，在流程图画布上绘制平行网关节点
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                processDiagramCanvas.drawParallelGateway(graphicInfo, scaleFactor);
            }
        });

        // event based gateway
        // 将事件网关的绘制指令添加到活动绘制指令映射中
        activityDrawInstructions.put(EventGateway.class, new ActivityDrawInstruction() {

            /**
             * 绘制事件网关图形
             * @param processDiagramCanvas 自定义的流程图画布，用于实际绘制图形
             * @param bpmnModel BPMN模型，包含流程节点信息和图形信息
             * @param flowNode 流程节点，这里是事件网关节点
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取流程节点的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 在画布上绘制事件网关，使用获取到的图形信息和缩放比例
                processDiagramCanvas.drawEventBasedGateway(graphicInfo, scaleFactor);
            }
        });

        // Boundary timer
        // 将边界事件的绘制指令添加到活动绘制指令映射中
        activityDrawInstructions.put(BoundaryEvent.class, new ActivityDrawInstruction() {

            /**
             * 绘制边界事件图形
             * 此方法根据流程节点的类型和事件定义绘制相应的图形表示
             *
             * @param processDiagramCanvas 用于绘制流程图的画布对象
             * @param bpmnModel BPMN模型，包含流程的所有信息
             * @param flowNode 具体的流程节点，这里是边界事件
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取流程节点的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 将流程节点转换为边界事件
                BoundaryEvent boundaryEvent = (BoundaryEvent) flowNode;
                // 检查边界事件的事件定义，并根据事件类型绘制相应的图形
                if (boundaryEvent.getEventDefinitions() != null && !boundaryEvent.getEventDefinitions().isEmpty()) {
                    // 获取第一个事件定义
                    EventDefinition eventDefinition = boundaryEvent.getEventDefinitions().get(0);
                    // 根据不同的事件定义类型绘制不同的边界事件图形
                    if (eventDefinition instanceof TimerEventDefinition) {
                        processDiagramCanvas.drawCatchingTimerEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);

                    } else if (eventDefinition instanceof ConditionalEventDefinition) {
                        processDiagramCanvas.drawCatchingConditionalEvent(graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);

                    } else if (eventDefinition instanceof ErrorEventDefinition) {
                        processDiagramCanvas.drawCatchingErrorEvent(graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);

                    } else if (eventDefinition instanceof EscalationEventDefinition) {
                        processDiagramCanvas.drawCatchingEscalationEvent(graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);

                    } else if (eventDefinition instanceof SignalEventDefinition) {
                        processDiagramCanvas.drawCatchingSignalEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);

                    } else if (eventDefinition instanceof MessageEventDefinition) {
                        processDiagramCanvas.drawCatchingMessageEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);

                    } else if (eventDefinition instanceof CompensateEventDefinition) {
                        processDiagramCanvas.drawCatchingCompensateEvent(graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);
                    }

                } else {
                    // 处理没有事件定义，但有扩展元素的情况
                    List<ExtensionElement> eventTypeElements = boundaryEvent.getExtensionElements().get("eventType");
                    if (eventTypeElements != null && eventTypeElements.size() > 0) {
                        processDiagramCanvas.drawCatchingEventRegistryEvent(flowNode.getName(), graphicInfo, boundaryEvent.isCancelActivity(), scaleFactor);
                    }
                }
            }
        });

        // subprocess
        // 将流程图绘制指令映射到活动上
        activityDrawInstructions.put(SubProcess.class, new ActivityDrawInstruction() {

            /**
             * 绘制子流程节点的图形
             *
             * @param processDiagramCanvas 用于绘制流程图的画布对象
             * @param bpmnModel BPMN模型对象，包含流程的所有信息
             * @param flowNode 流程节点对象，表示需要绘制的子流程节点
             *
             * 此方法根据子流程节点的图形信息判断是否展开子流程，并根据判断结果绘制相应的子流程图形
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取流程节点的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 根据图形信息判断是否展开子流程
                if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) {
                    // 如果子流程未展开，则绘制折叠的子流程
                    processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor);
                } else {
                    // 如果子流程已展开，则绘制展开的子流程
                    processDiagramCanvas.drawExpandedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor);
                }
            }
        });

        // transaction
        // 将交易活动的绘制指令添加到活动绘制指令集合中
        activityDrawInstructions.put(Transaction.class, new ActivityDrawInstruction() {


            /**
             * 绘制交易活动的图形表示
             * @param processDiagramCanvas 用于绘制流程图的画布
             * @param bpmnModel BPMN模型，包含流程的所有信息
             * @param flowNode 需要绘制的FlowNode节点
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取节点的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());

                // 判断当前节点是否被展开，如果节点是折叠的，则绘制折叠的子流程
                if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) {
                    processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor);
                } else {
                    // 否则，绘制展开的交易活动
                    processDiagramCanvas.drawExpandedTransaction(flowNode.getName(), graphicInfo, scaleFactor);
                }
            }
        });

        // Event subprocess
        // 将事件子流程的绘制指令添加到活动绘制指令映射中
        activityDrawInstructions.put(EventSubProcess.class, new ActivityDrawInstruction() {

            /**
             * 绘制事件子流程的图形表示
             * @param processDiagramCanvas 自定义的流程图画布，用于实际绘制流程节点
             * @param bpmnModel BPMN模型，包含流程节点的所有信息
             * @param flowNode 需要绘制的流程节点
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取流程节点的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 判断当前子流程是否被展开，若未展开，则绘制折叠的子流程
                if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) {
                    // 绘制折叠的子流程
                    processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, true, scaleFactor);
                } else {
                    // 绘制展开的子流程
                    processDiagramCanvas.drawExpandedSubProcess(flowNode.getName(), graphicInfo, true, scaleFactor);
                }
            }
        });

        // Adhoc subprocess
        // 将自定义的绘图指令添加到活动绘图指令映射中，针对AdhocSubProcess类的自定义流程图绘制方式
        activityDrawInstructions.put(AdhocSubProcess.class, new ActivityDrawInstruction() {


            /**
             * 绘制自定义子流程节点的逻辑
             * 根据子流程的展开状态，决定是绘制展开还是折叠的子流程图形
             *
             * @param processDiagramCanvas 用于绘制流程图的画布对象
             * @param bpmnModel 流程模型，包含流程的所有信息和图形布局
             * @param flowNode 流程节点对象，代表要绘制的子流程节点
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取流程节点的图形信息，包括位置、尺寸和展开状态
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 根据图形信息的展开状态，决定绘制展开还是折叠的子流程
                if (graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) {
                    // 如果子流程是折叠的，则绘制折叠的子流程图形
                    processDiagramCanvas.drawCollapsedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor);
                } else {
                    // 如果子流程是展开的，则绘制展开的子流程图形
                    processDiagramCanvas.drawExpandedSubProcess(flowNode.getName(), graphicInfo, false, scaleFactor);
                }
            }
        });

        // call activity
        // 将调用活动的绘制指令添加到活动绘制指令映射中
        activityDrawInstructions.put(CallActivity.class, new ActivityDrawInstruction() {

            /**
             * 绘制自定义默认流程图画布上的调用活动
             *
             * @param processDiagramCanvas 流程图画布，用于绘制流程节点和连线
             * @param bpmnModel BPMN模型，包含流程的所有信息
             * @param flowNode 流节点，此处特指调用活动节点
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode) {
                // 获取流程节点的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());
                // 绘制折叠的调用活动
                processDiagramCanvas.drawCollapsedCallActivity(flowNode.getName(), graphicInfo, scaleFactor);
            }
        });

        // text annotation
        // 为TextAnnotation类提供定制的绘制指令
        artifactDrawInstructions.put(TextAnnotation.class, new ArtifactDrawInstruction() {

            /**
             * 绘制文本注释对象
             *
             * @param processDiagramCanvas 过程图 canvas，用于绘制注释
             * @param bpmnModel BPMN 模型，用于获取图形信息
             * @param artifact 注释对象，此处特化为 TextAnnotation
             *
             * 本方法专用于在画布上绘制文本注释，它从 BPMN 模型中获取注释的图形信息，并使用这些信息
             * 以及缩放因子来确定如何在画布上呈现文本注释。
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact) {
                // 获取注释对象的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(artifact.getId());
                // 将 artifact 转换为 TextAnnotation
                TextAnnotation textAnnotation = (TextAnnotation) artifact;
                // 在画布上绘制文本注释
                processDiagramCanvas.drawTextAnnotation(textAnnotation.getText(), graphicInfo, scaleFactor);
            }
        });

        // association
        // 将Association类的绘图指令添加到映射中
        artifactDrawInstructions.put(Association.class, new ArtifactDrawInstruction() {

            /**
             * 绘制自定义默认流程图画布上的关联Artifact
             *
             * @param processDiagramCanvas 流程图画布，用于绘制关联
             * @param bpmnModel BPMN模型，包含流程图的所有元素和图形信息
             * @param artifact 关联Artifact，实现此方法的目的就是绘制它
             */
            @Override
            public void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact) {
                // 将artifact转换为Association类型
                Association association = (Association) artifact;
                // 获取关联的起始和结束引用
                String sourceRef = association.getSourceRef();
                String targetRef = association.getTargetRef();

                // source and target can be instance of FlowElement or Artifact
                // 根据引用获取起始和结束元素，它们可以是FlowElement或Artifact类型
                BaseElement sourceElement = bpmnModel.getFlowElement(sourceRef);
                BaseElement targetElement = bpmnModel.getFlowElement(targetRef);
                if (sourceElement == null) {
                    sourceElement = bpmnModel.getArtifact(sourceRef);
                }
                if (targetElement == null) {
                    targetElement = bpmnModel.getArtifact(targetRef);
                }
                // 获取关联的图形信息列表，并进行连线优化处理
                List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(artifact.getId());
                graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement, targetElement, graphicInfoList);
                // 初始化用于存储x坐标和y坐标的数组
                int[] xPoints = new int[graphicInfoList.size()];
                int[] yPoints = new int[graphicInfoList.size()];
                // 遍历图形信息列表，提取坐标点
                for (int i = 1; i < graphicInfoList.size(); i++) {
                    GraphicInfo graphicInfo = graphicInfoList.get(i);
                    GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1);

                    if (i == 1) {
                        // 第一个点需要特殊处理，存储前一个图形信息的坐标
                        xPoints[0] = (int) previousGraphicInfo.getX();
                        yPoints[0] = (int) previousGraphicInfo.getY();
                    }
                    // 存储当前图形信息的坐标
                    xPoints[i] = (int) graphicInfo.getX();
                    yPoints[i] = (int) graphicInfo.getY();
                }

                // 获取关联方向，并在画布上绘制关联
                AssociationDirection associationDirection = association.getAssociationDirection();
                processDiagramCanvas.drawAssociation(xPoints, yPoints, associationDirection, false, scaleFactor);
            }
        });
    }

    /**
     * 生成流程图
     * 该方法根据BPMN模型生成流程图，并允许高亮显示特定的活动和流程
     * 此外，还可以自定义字体、使用自定义类加载器、调整比例因子以及选择是否绘制序列流名称
     *
     * @param bpmnModel BPMN模型对象，流程图生成的基础
     * @param imageType 图片类型，如"png"、"jpg"等
     * @param highLightedActivities 需要高亮显示的活动列表
     * @param highLightedFlows 需要高亮显示的流程列表
     * @param activityFontName 活动字体名称
     * @param labelFontName 标签字体名称
     * @param annotationFontName 注释字体名称
     * @param customClassLoader 自定义类加载器，用于处理自定义扩展
     * @param scaleFactor 尺寸缩放因子，用于调整流程图的大小
     * @param drawSequenceFlowNameWithNoLabelDI 是否在没有标签的情况下绘制序列流名称
     * @return 返回生成的流程图的输入流
     */
    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities, List<String> highLightedFlows,
                                       String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {

        return generateProcessDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows,
            activityFontName, labelFontName, annotationFontName, customClassLoader, scaleFactor, drawSequenceFlowNameWithNoLabelDI).generateImage(imageType);
    }

    /**
     * 生成流程图
     * 此方法用作生成流程图的快捷方式，它继承自基类的生成逻辑，
     * 但省略了部分参数，主要用于简化调用过程
     *
     * @param bpmnModel BPMN模型，用于生成流程图的基础
     * @param imageType 图像类型，决定生成的流程图是何种格式（如PNG、JPEG等）
     * @param highLightedActivities 需要高亮的活动列表，用于在流程图中突出显示特定活动
     * @param highLightedFlows 需要高亮的流程列表，用于在流程图中突出显示特定流程
     * @param drawSequenceFlowNameWithNoLabelDI 布尔值，指示是否在没有标签的情况下绘制序列流名称
     * @return 返回一个输入流，包含生成的流程图数据
     */
    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities, List<String> highLightedFlows, boolean drawSequenceFlowNameWithNoLabelDI) {
        // 调用父类的generateDiagram方法，进行流程图的生成
        // 此处的实现省略了部分参数，是为了提供一个简化版本的接口，便于快速生成流程图
        return generateDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows, null, null, null, null, 1.0, drawSequenceFlowNameWithNoLabelDI);
    }

    /**
     * 生成流程图
     * 此方法根据BPMN模型生成图表，并允许高亮显示特定的活动和流程
     * 它通过调用另一个重载版本的generateDiagram方法来实现，传递了为null的参数以处理默认行为
     *
     * @param bpmnModel BPMN模型，表示业务流程的结构和行为
     * @param imageType 图像类型，指定生成图表的格式（如PNG，JPEG等）
     * @param highLightedActivities 高亮显示的活动列表，用于强调流程中的特定活动
     * @param highLightedFlows 高亮显示的流程列表，用于强调流程中的特定流程路径
     * @param scaleFactor 缩放因子，用于调整生成图表的大小
     * @param drawSequenceFlowNameWithNoLabelDI 布尔值，指定是否绘制没有标签的序列流名称
     * @return 返回一个InputStream，包含生成的图表数据
     */
    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType,
                                       List<String> highLightedActivities, List<String> highLightedFlows, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows, null, null, null, null, scaleFactor, drawSequenceFlowNameWithNoLabelDI);
    }

    /**
     * 生成流程图
     *
     * 此方法根据提供的BpmnModel生成流程图，并允许高亮特定活动
     * 它重载了generateDiagram方法，为序列流名称的绘制提供了额外的控制选项
     *
     * @param bpmnModel BPMN模型，用于生成流程图
     * @param imageType 生成的图像类型，例如PNG或JPEG
     * @param highLightedActivities 需要高亮显示的活动列表
     * @param drawSequenceFlowNameWithNoLabelDI 如果未设置标签，则是否绘制序列流名称
     * @return 生成的流程图对应的输入流
     */
    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateDiagram(bpmnModel, imageType, highLightedActivities, Collections.emptyList(), drawSequenceFlowNameWithNoLabelDI);
    }

    /**
     * 根据提供的Bpmn模型生成流程图
     * 此方法重载了generateDiagram方法，允许调用者指定一组高亮活动
     * 它通过调用另一个具有更多参数的generateDiagram方法实现生成流程图的功能
     *
     * @param bpmnModel Bpmn模型实例，表示流程图的结构和内容
     * @param imageType 图像类型，指定生成的流程图的格式，如PNG、JPEG等
     * @param highLightedActivities 需要高亮显示的活动列表，用于在流程图中突出显示特定活动
     * @param scaleFactor 缩放因子，用于调整生成的流程图的大小
     * @param drawSequenceFlowNameWithNoLabelDI 布尔值，控制是否在没有标签的情况下绘制序列流名称
     * @return 返回一个输入流，包含生成的流程图数据
     */
    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateDiagram(bpmnModel, imageType, highLightedActivities, Collections.emptyList(), scaleFactor, drawSequenceFlowNameWithNoLabelDI);
    }

    /**
     * 根据提供的BPMN模型生成流程图
     * 此方法重载了generateDiagram方法，提供默认行为，不包括自定义扩展包和缩放比例
     *
     * @param bpmnModel BPMN模型的实例，用于作为生成流程图的基础
     * @param imageType 流程图的图像类型，例如PNG、JPEG等
     * @param activityFontName 活动名称的字体名称
     * @param labelFontName 事件标签的字体名称
     * @param annotationFontName 注释的字体名称
     * @param customClassLoader 自定义类加载器，用于查找扩展包
     * @param drawSequenceFlowNameWithNoLabelDI 布尔值，确定是否在没有标签的情况下绘制序列流名称
     * @return 返回一个输入流，其中包含生成的流程图图像
     */
    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, String activityFontName,
                                       String labelFontName, String annotationFontName, ClassLoader customClassLoader, boolean drawSequenceFlowNameWithNoLabelDI) {

        return generateDiagram(bpmnModel, imageType, Collections.emptyList(), Collections.emptyList(),
            activityFontName, labelFontName, annotationFontName, customClassLoader, 1.0, drawSequenceFlowNameWithNoLabelDI);
    }

    /**
     * 根据提供的参数生成BPMN图的图像流
     * 此方法重载了generateDiagram方法，允许定制BPMN图的外观和行为
     *
     * @param bpmnModel BPMN模型，用于生成图表的基础
     * @param imageType 图像类型，指定生成图表的格式（如PNG、JPEG等）
     * @param activityFontName 活动字体名称，用于在图表中显示活动名称的字体
     * @param labelFontName 标签字体名称，用于在图表中显示标签的字体
     * @param annotationFontName 注释字体名称，用于在图表中显示注释的字体
     * @param customClassLoader 自定义类加载器，可能包含额外的定义或定制逻辑
     * @param scaleFactor 缩放系数，用于调整图表的大小
     * @param drawSequenceFlowNameWithNoLabelDI 布尔值，指示是否在没有标签的情况下绘制序列流名称
     * @return 生成的BPMN图的图像流
     */
    @Override
    public InputStream generateDiagram(BpmnModel bpmnModel, String imageType, String activityFontName,
                                       String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {

        return generateDiagram(bpmnModel, imageType, Collections.emptyList(), Collections.emptyList(),
            activityFontName, labelFontName, annotationFontName, customClassLoader, scaleFactor, drawSequenceFlowNameWithNoLabelDI);
    }

    /**
     * 生成PNG格式的流程图
     *
     * @param bpmnModel BPMN模型，用于生成流程图的基础
     * @param drawSequenceFlowNameWithNoLabelDI 是否绘制没有标签的序列流名称，控制流程图的详细程度
     * @return 返回一个输入流，该流包含生成的PNG格式流程图
     */
    @Override
    public InputStream generatePngDiagram(BpmnModel bpmnModel, boolean drawSequenceFlowNameWithNoLabelDI) {
        // 调用重载的generatePngDiagram方法，传入bpmnModel、默认缩放比例1.0和drawSequenceFlowNameWithNoLabelDI参数
        // 这里的默认缩放比例适用于大多数情况，使得流程图既清晰又不失真
        return generatePngDiagram(bpmnModel, 1.0, drawSequenceFlowNameWithNoLabelDI);
    }

    /**
     * 根据提供的Bpmn模型生成PNG格式的图表
     * 此方法允许将BPMN模型视觉化为PNG图像，参数允许调整图像的缩放比例
     * 和是否绘制没有标签的序列流名称
     *
     * @param bpmnModel BPMN模型对象，包含了流程图的结构和内容
     * @param scaleFactor 图像缩放因子，用于调整输出图像的大小
     * @param drawSequenceFlowNameWithNoLabelDI 布尔值，指示是否应该在没有标签的情况下绘制序列流的名称
     * @return 返回一个输入流，其中包含生成的PNG图像数据
     */
    @Override
    public InputStream generatePngDiagram(BpmnModel bpmnModel, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateDiagram(bpmnModel, "png", Collections.emptyList(), Collections.emptyList(), scaleFactor, drawSequenceFlowNameWithNoLabelDI);
    }

    /**
     * 生成BPMN模型的JPEG格式图表
     * 该方法提供了一种将BPMN模型转换为JPEG图像的功能，用于图表可视化
     *
     * @param bpmnModel BpmnModel对象，包含要生成图表的BPMN模型
     * @return 返回一个InputStream，其中包含生成的JPEG格式图表数据
     *
     * 注意：此方法使用默认的缩放比例1.0和不使用网格的设置来生成图表
     */
    @Override
    public InputStream generateJpgDiagram(BpmnModel bpmnModel) {
        // 调用重载的generateJpgDiagram方法，并使用默认的缩放比例和网格设置
        return generateJpgDiagram(bpmnModel, 1.0, false);
    }

    /**
     * 根据BPMN模型生成JPEG格式的图表
     *
     * @param bpmnModel BPMN模型对象，包含流程图的所有信息
     * @param scaleFactor 缩放因子，用于调整生成图表的大小
     * @param drawSequenceFlowNameWithNoLabelDI 指示是否在没有标签的情况下绘制序列流名称的布尔值
     * @return 返回一个InputStream，其中包含生成的JPEG格式图表数据
     *
     * 此方法专门用于生成JPEG格式的流程图表，允许用户通过指定缩放因子和序列流绘制选项来自定义输出
     * 它通过调用通用的generateDiagram方法实现，但仅针对JPEG格式进行优化
     */
    @Override
    public InputStream generateJpgDiagram(BpmnModel bpmnModel, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
        return generateDiagram(bpmnModel, "jpg", Collections.emptyList(), Collections.emptyList(), drawSequenceFlowNameWithNoLabelDI);
    }

    /**
     * 根据提供的BPMN模型生成图像
     * 此方法允许对BPMN模型进行可视化，同时允许突出显示特定的活动和流程
     * 还可以自定义字体、类加载器、缩放因子以及是否绘制无标签的序列流名称
     * 最终生成的是一个BufferedImage对象，可以方便地进行进一步的图像处理或保存
     *
     * @param bpmnModel BPMN模型，包含流程图的所有必要信息
     * @param imageType 希望生成的图像类型，如"png"、"jpg"等
     * @param highLightedActivities 需要突出显示的活动列表，为空则不进行突出显示
     * @param highLightedFlows 需要突出显示的流程列表，为空则不进行突出显示
     * @param activityFontName 活动字体名称，用于在图像中表示活动
     * @param labelFontName 标签字体名称，用于在图像中表示标签
     * @param annotationFontName 注释字体名称，用于在图像中表示注释
     * @param customClassLoader 自定义类加载器，用于加载可能需要的自定义类或资源
     * @param scaleFactor 缩放因子，用于调整生成图像的大小
     * @param drawSequenceFlowNameWithNoLabelDI 布尔值，决定是否绘制没有标签的序列流名称
     * @return 生成的BufferedImage对象，表示可视化后的BPMN模型
     */
    public BufferedImage generateImage(BpmnModel bpmnModel, String imageType, List<String> highLightedActivities, List<String> highLightedFlows,
                                       String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {

        return generateProcessDiagram(bpmnModel, imageType, highLightedActivities, highLightedFlows,
            activityFontName, labelFontName, annotationFontName, customClassLoader, scaleFactor, drawSequenceFlowNameWithNoLabelDI).generateBufferedImage(imageType);
    }

    /**
     * 生成BPMN模型的图像，并突出显示指定的活动和流程
     * 该方法通过调用另一个重载的generateImage方法来实现，提供额外的自定义选项
     *
     * @param bpmnModel BPMN模型对象，包含了流程图的所有数据
     * @param imageType 图像类型，如PNG、JPEG等，决定了输出图像的格式
     * @param highLightedActivities 需要突出显示的活动列表，活动通过ID来标识
     * @param highLightedFlows 需要突出显示的流程列表，流程通过ID来标识
     * @param scaleFactor 缩放因子，用于调整图像的大小，值大于1表示放大图像
     * @param drawSequenceFlowNameWithNoLabelDI 布尔值，决定是否绘制没有标签的序列流名称
     * @return 返回一个BufferedImage对象，包含了绘制的图像内容
     */
    public BufferedImage generateImage(BpmnModel bpmnModel, String imageType,
                                       List<String> highLightedActivities, List<String> highLightedFlows, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
        // 调用重载的generateImage方法，提供更多自定义参数，这里将未提及的参数设为null
        return generateImage(bpmnModel, imageType, highLightedActivities, highLightedFlows, null, null, null, null, scaleFactor, drawSequenceFlowNameWithNoLabelDI);
    }

    /**
     * 为给定的BPMN模型生成PNG格式的图像
     *
     * @param bpmnModel BpmnModel对象，包含要生成图像的BPMN模型
     * @param scaleFactor 缩放因子，用于确定生成图像的大小
     * @return 返回一个BufferedImage对象，表示生成的PNG图像
     *
     * 此方法是generateImage方法的特化，用于生成PNG图像文件它使用提供的BPMN模型和缩放因子来生成图像，
     * 并返回表示该图像的BufferedImage对象由于PNG图像不支持透明背景，因此传递给generateImage方法的参数中，
     * 表示透明背景的标志被设置为false
     */
    @Override
    public BufferedImage generatePngImage(BpmnModel bpmnModel, double scaleFactor) {
        return generateImage(bpmnModel, "png", Collections.emptyList(), Collections.emptyList(), scaleFactor, false);
    }

    /**
     * 生成流程图
     *
     * @param bpmnModel BPMN模型，包含流程的所有信息
     * @param imageType 图片类型，如PNG或JPEG
     * @param highLightedActivities 需要高亮的活动列表
     * @param highLightedFlows 需要高亮的流列表
     * @param activityFontName 活动字体名称
     * @param labelFontName 标签字体名称
     * @param annotationFontName 注释字体名称
     * @param customClassLoader 自定义类加载器，用于加载特定类
     * @param scaleFactor 缩放因子，用于调整流程图大小
     * @param drawSequenceFlowNameWithNoLabelDI 是否绘制没有标签的序列流名称
     * @return 生成的流程图画布对象
     */
    protected CustomDefaultProcessDiagramCanvas generateProcessDiagram(BpmnModel bpmnModel, String imageType,
                                                                       List<String> highLightedActivities, List<String> highLightedFlows,
                                                                       String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader, double scaleFactor, boolean drawSequenceFlowNameWithNoLabelDI) {
        // 准备BPMN模型，可能包括一些预处理步骤
        prepareBpmnModel(bpmnModel);

        // 初始化流程图画布
        CustomDefaultProcessDiagramCanvas processDiagramCanvas = initProcessDiagramCanvas(bpmnModel, imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);

        // Draw pool shape, if process is participant in collaboration
        // 绘制泳池（如果流程是协作中的参与者）
        // 遍历模型中的所有池（Pool），以绘制每个池在流程图画布上的表示
        for (Pool pool : bpmnModel.getPools()) {
            // 获取当前池的图形信息，以便确定其在画布上的位置和大小
            GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId());
            // 使用池的名称和图形信息，调用drawPoolOrLane方法绘制池
            // 通过缩放因子调整绘制的大小，以适应流程图画布
            processDiagramCanvas.drawPoolOrLane(pool.getName(), graphicInfo, scaleFactor);
        }

        // Draw lanes
        // 绘制泳道
        // 遍历模型中的所有流程
        for (Process process : bpmnModel.getProcesses()) {
            // 遍历流程中的所有泳道
            for (Lane lane : process.getLanes()) {
                // 获取泳道的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(lane.getId());
                // 绘制泳道或泳池
                processDiagramCanvas.drawPoolOrLane(lane.getName(), graphicInfo, scaleFactor);
            }
        }

        // Draw activities and their sequence-flows
        // 绘制活动及其序列流
        for (Process process : bpmnModel.getProcesses()) {
            // 遍历流程中的所有流节点
            for (FlowNode flowNode : process.findFlowElementsOfType(FlowNode.class)) {
                // 检查流节点是否不属于折叠的子流程，以避免在图中重复绘制
                if (!isPartOfCollapsedSubProcess(flowNode, bpmnModel)) {
                    // 绘制活动节点，但不包括那些属于折叠子流程的节点
                    drawActivity(processDiagramCanvas, bpmnModel, flowNode, highLightedActivities, highLightedFlows, scaleFactor, drawSequenceFlowNameWithNoLabelDI);
                }
            }
        }

        // Draw artifacts
        // 绘制工件（如文本注释、关联等）
        for (Process process : bpmnModel.getProcesses()) {

            // 绘制流程中的所有工件（Artifacts）
            for (Artifact artifact : process.getArtifacts()) {
                drawArtifact(processDiagramCanvas, bpmnModel, artifact);
            }

            // 查找流程中的所有子流程（SubProcesses）
            List<SubProcess> subProcesses = process.findFlowElementsOfType(SubProcess.class, true);
            if (subProcesses != null) {
                // 遍历所有子流程
                for (SubProcess subProcess : subProcesses) {
                    // 获取子流程的图形信息
                    GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(subProcess.getId());
                    // 如果图形信息存在且子流程被展开，则跳过该子流程
                    if (graphicInfo != null && graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) {
                        continue;
                    }

                    // 检查子流程是否是折叠子流程的一部分
                    if (!isPartOfCollapsedSubProcess(subProcess, bpmnModel)) {
                        // 绘制子流程中的所有工件
                        for (Artifact subProcessArtifact : subProcess.getArtifacts()) {
                            drawArtifact(processDiagramCanvas, bpmnModel, subProcessArtifact);
                        }
                    }
                }
            }
        }

        // 返回流程图画布对象
        return processDiagramCanvas;
    }

    /**
     * 准备并解析BPMN模型
     *
     * @param bpmnModel BpmnModel类型的模型，用于描述业务流程的执行顺序和细节
     * 该方法的主要目的是在流程定义之前，对BPMN模型进行必要的预处理或解析，
     * 以便后续流程能够顺利执行具体任务
     */
    protected void prepareBpmnModel(BpmnModel bpmnModel) {

        // Need to make sure all elements have positive x and y.
        // Check all graphicInfo and update the elements accordingly
        // 创建一个存储所有图形信息的列表
        List<GraphicInfo> allGraphicInfos = new ArrayList<>();
        // 如果模型的位置映射不为空，将位置信息添加到allGraphicInfos中
        if (bpmnModel.getLocationMap() != null) {
            allGraphicInfos.addAll(bpmnModel.getLocationMap().values());
        }
        // 如果模型的标签位置映射不为空，将标签位置信息添加到allGraphicInfos中
        if (bpmnModel.getLabelLocationMap() != null) {
            allGraphicInfos.addAll(bpmnModel.getLabelLocationMap().values());
        }
        // 如果模型的流位置映射不为空，将流位置信息添加到allGraphicInfos中
        if (bpmnModel.getFlowLocationMap() != null) {
            for (List<GraphicInfo> flowGraphicInfos : bpmnModel.getFlowLocationMap().values()) {
                allGraphicInfos.addAll(flowGraphicInfos);
            }
        }

        // 当存在图形信息时，开始计算布局参数
        if (allGraphicInfos.size() > 0) {

            // 初始化平移标志
            boolean needsTranslationX = false;
            boolean needsTranslationY = false;

            // 初始化最低X坐标
            double lowestX = 0.0;
            // 初始化最低Y坐标
            double lowestY = 0.0;

            // Collect lowest x and y
            // 遍历所有图形信息，以确定最低的X和Y坐标
            for (GraphicInfo graphicInfo : allGraphicInfos) {

                // 获取当前图形的X和Y坐标
                double x = graphicInfo.getX();
                double y = graphicInfo.getY();

                // 如果当前X坐标小于已知的最低X坐标
                if (x < lowestX) {
                    // 标记需要在X轴上平移
                    needsTranslationX = true;
                    // 更新最低X坐标
                    lowestX = x;
                }
                // 如果当前Y坐标小于已知的最低Y坐标
                if (y < lowestY) {
                    // 标记需要在Y轴上平移
                    needsTranslationY = true;
                    // 更新最低Y坐标
                    lowestY = y;
                }

            }

            // Update all graphicInfo objects
            // 检查是否需要沿X或Y轴进行平移
            if (needsTranslationX || needsTranslationY) {

                // 计算沿X轴和Y轴平移的距离
                double translationX = Math.abs(lowestX);
                double translationY = Math.abs(lowestY);

                // 遍历所有图形信息，根据需要进行平移
                for (GraphicInfo graphicInfo : allGraphicInfos) {
                    // 如果需要沿X轴平移，则更新图形的X坐标
                    if (needsTranslationX) {
                        graphicInfo.setX(graphicInfo.getX() + translationX);
                    }
                    // 如果需要沿Y轴平移，则更新图形的Y坐标
                    if (needsTranslationY) {
                        graphicInfo.setY(graphicInfo.getY() + translationY);
                    }
                }
            }
        }
    }

    /**
     * 在流程图画布上绘制活动节点
     *
     * @param processDiagramCanvas 流程图画布，用于实际绘制操作
     * @param bpmnModel BPMN模型，提供流程图的模型数据
     * @param flowNode 流程节点，当前需要绘制的节点
     * @param highLightedActivities 高亮的活动列表，用于突出显示某些活动节点
     * @param highLightedFlows 高亮的流列表，用于突出显示某些流程线
     * @param scaleFactor 缩放因子，用于控制绘制流程图的大小
     * @param drawSequenceFlowNameWithNoLabelDI 是否绘制没有标签的序列流名称，用于控制流程线名称的显示逻辑
     */
    protected void drawActivity(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel,
                                FlowNode flowNode, List<String> highLightedActivities, List<String> highLightedFlows, double scaleFactor, Boolean drawSequenceFlowNameWithNoLabelDI) {

        // 根据flowNode的类型获取对应的绘图指令
        ActivityDrawInstruction drawInstruction = activityDrawInstructions.get(flowNode.getClass());
        // 如果存在对应的绘图指令，则调用draw方法绘制流程节点
        if (drawInstruction != null) {

            // 在流程图画布上绘制流程节点
            // 此处调用drawInstruction的draw方法，目的是根据流程节点的类型和属性，
            // 在给定的画布上绘制出相应的图形和连接线，从而构成整个业务流程图
            drawInstruction.draw(processDiagramCanvas, bpmnModel, flowNode);

            // Gather info on the multi instance marker
            // 初始化顺序和并行多实例标志为false
            boolean multiInstanceSequential = false;
            boolean multiInstanceParallel = false;
            // 初始化折叠标志为false
            boolean collapsed = false;

            // 检查flowNode是否为Activity类型
            if (flowNode instanceof Activity) {
                Activity activity = (Activity) flowNode;
                // 获取活动的多实例循环特性
                MultiInstanceLoopCharacteristics multiInstanceLoopCharacteristics = activity.getLoopCharacteristics();
                // 如果多实例循环特性不为空
                if (multiInstanceLoopCharacteristics != null) {
                    // 设置顺序多实例标志
                    multiInstanceSequential = multiInstanceLoopCharacteristics.isSequential();
                    // 设置并行多实例标志，与顺序多实例标志取反
                    multiInstanceParallel = !multiInstanceSequential;
                }
            }

            // Gather info on the collapsed marker
            // 获取流节点的图形信息
            GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());

            // 根据流节点的类型决定是否折叠
            if (flowNode instanceof SubProcess) {
                // 对于子流程节点，判断其是否折叠
                collapsed = graphicInfo.getExpanded() != null && !graphicInfo.getExpanded();
            } else if (flowNode instanceof CallActivity) {
                // 对于调用活动节点，直接视为折叠
                collapsed = true;
            }

            // 当缩放因子为1.0时，执行绘制操作
            if (scaleFactor == 1.0) {
                // Actually draw the markers
                // 实际绘制活动标记
                processDiagramCanvas.drawActivityMarkers((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight(),
                    multiInstanceSequential, multiInstanceParallel, collapsed);
            }

            // Draw highlighted activities
            // 如果当前流节点的ID存在于高亮显示的活动集合中
            if (highLightedActivities.contains(flowNode.getId())) {
                // 在流程图画布上使用红色高亮绘制该流节点
                drawHighLightRed(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()));
            } else if (highLightedActivities.contains(Color.RED.toString() + flowNode.getId())) {
                // 否则，如果以红色高亮前缀加上流节点ID的字符串存在于集合中
                // 在流程图画布上使用默认高亮绘制该流节点
                drawHighLight(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()));
            }

        } else if (flowNode instanceof Task) {
            // 如果流节点是任务类型，则使用针对任务的绘图指令进行绘制
            activityDrawInstructions.get(Task.class).draw(processDiagramCanvas, bpmnModel, flowNode);

            // 检查当前任务节点是否在高亮显示的活动列表中
            if (highLightedActivities.contains(flowNode.getId())) {
                // 如果当前节点高亮显示，则绘制红色高亮图形信息
                drawHighLightRed(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()));
            } else if (highLightedActivities.contains(Color.RED.toString() + flowNode.getId())) {
                // 如果当前节点是红色高亮活动，则绘制默认高亮图形信息
                drawHighLight(processDiagramCanvas, bpmnModel.getGraphicInfo(flowNode.getId()));
            }
        }


        // Outgoing transitions of activity
        // 遍历当前流节点的所有出向流，以处理流向下一个节点的序列
        for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
            // 检查当前序列是否应被高亮显示
            boolean highLighted = (highLightedFlows.contains(sequenceFlow.getId()));

            // 定义默认流变量，用于存储流节点的默认流向
            String defaultFlow = null;

            // 根据流节点的类型（活动或网关）获取默认流向
            if (flowNode instanceof Activity) {
                // 如果流节点是活动类型，则获取该活动的默认流向
                defaultFlow = ((Activity) flowNode).getDefaultFlow();
            } else if (flowNode instanceof Gateway) {
                // 如果流节点是网关类型，则获取该网关的默认流向
                defaultFlow = ((Gateway) flowNode).getDefaultFlow();
            }

            // 初始化isDefault标志为false，用于后续判断当前序列流是否为默认流
            boolean isDefault = false;
            // 检查当前序列流是否被标记为默认流，如果是，则将isDefault设置为true
            if (defaultFlow != null && defaultFlow.equalsIgnoreCase(sequenceFlow.getId())) {
                isDefault = true;
            }
            // 确定是否需要绘制条件指示器：只有当序列流有条件表达式且当前节点不是网关时，才绘制条件指示器
            // 这一判断逻辑有助于在流程图中更清晰地标识出条件流，避免在网关节点上重复显示条件信息
            boolean drawConditionalIndicator = sequenceFlow.getConditionExpression() != null && sequenceFlow.getConditionExpression().trim().length() > 0 && !(flowNode instanceof Gateway);

            // 获取序列流的源引用
            String sourceRef = sequenceFlow.getSourceRef();
            // 获取序列流的目标引用
            String targetRef = sequenceFlow.getTargetRef();
            // 根据源引用获取源FlowElement
            FlowElement sourceElement = bpmnModel.getFlowElement(sourceRef);
            // 根据目标引用获取目标FlowElement
            FlowElement targetElement = bpmnModel.getFlowElement(targetRef);
            // 获取序列流的位置图形信息列表
            List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId());
            // 检查图形信息列表是否非空，以确保后续操作有数据可处理
            if (graphicInfoList != null && graphicInfoList.size() > 0) {
                // 对图形信息列表进行连接优化，这是流程图规范化和美观化的重要一步
                graphicInfoList = connectionPerfectionizer(processDiagramCanvas, bpmnModel, sourceElement, targetElement, graphicInfoList);

                // 初始化x坐标数组，用于存储图形信息的x坐标
                int[] xPoints = new int[graphicInfoList.size()];
                // 初始化y坐标数组，用于存储图形信息的y坐标
                int[] yPoints = new int[graphicInfoList.size()];

                // 遍历graphicInfoList列表中的元素，用于提取每个图形信息
                for (int i = 1; i < graphicInfoList.size(); i++) {
                    // 获取当前图形的信息
                    GraphicInfo graphicInfo = graphicInfoList.get(i);
                    // 获取前一个图形的信息
                    GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1);

                    // 对于列表中的第一个元素，初始化xPoints和yPoints数组的第一个元素
                    if (i == 1) {
                        xPoints[0] = (int) previousGraphicInfo.getX();
                        yPoints[0] = (int) previousGraphicInfo.getY();
                    }
                    // 将当前图形的x和y坐标转换为整数类型，并存储到相应的数组中
                    xPoints[i] = (int) graphicInfo.getX();
                    yPoints[i] = (int) graphicInfo.getY();
                }

                // 绘制流程图中的序列流
                // 该方法主要用于在流程图画布上绘制表示流程方向的序列流箭头
                // 包括处理坐标点、条件标识、默认流、高亮状态及缩放比例
                processDiagramCanvas.drawSequenceflow(xPoints, yPoints, drawConditionalIndicator, isDefault, highLighted, scaleFactor);

                // Draw sequenceflow label
                // 获取序列流的标签图形信息
                GraphicInfo labelGraphicInfo = bpmnModel.getLabelGraphicInfo(sequenceFlow.getId());
                if (labelGraphicInfo != null) {
                    // 如果标签图形信息存在，则绘制序列流的标签
                    processDiagramCanvas.drawLabel(sequenceFlow.getName(), labelGraphicInfo, false);
                } else {
                    // 如果标签图形信息不存在，并且配置了绘制无标签的序列流名称
                    if (drawSequenceFlowNameWithNoLabelDI) {
                        // 计算线的中心点图形信息
                        GraphicInfo lineCenter = getLineCenter(graphicInfoList);
                        // 在线的中心点绘制序列流的标签
                        processDiagramCanvas.drawLabel(sequenceFlow.getName(), lineCenter, false);
                    }

                }
            }
        }

        // Nested elements
        // 判断当前流程节点是否为流程元素容器
        if (flowNode instanceof FlowElementsContainer) {
            // 遍历当前流程节点容器中的所有流元素
            for (FlowElement nestedFlowElement : ((FlowElementsContainer) flowNode).getFlowElements()) {
                // 判断当前流元素是否为流节点，并且不属于折叠的子流程
                if (nestedFlowElement instanceof FlowNode && !isPartOfCollapsedSubProcess(nestedFlowElement, bpmnModel)) {
                    // 绘制满足条件的流节点及其关联的序列流
                    drawActivity(processDiagramCanvas, bpmnModel, (FlowNode) nestedFlowElement,
                        highLightedActivities, highLightedFlows, scaleFactor, drawSequenceFlowNameWithNoLabelDI);
                }
            }
        }
    }

    /**
     * This method makes coordinates of connection flow better.
     *
     * 优化连接的显示效果，使其在流程图中看起来更美观
     * 该方法主要通过考虑源元素和目标元素的形状和位置，来调整连接线的路径
     *
     * @param processDiagramCanvas 流程图画布，用于绘制和优化流程图
     * @param bpmnModel BPMN模型，包含流程图的详细信息
     * @param sourceElement 源元素，连接线的起点
     * @param targetElement 目标元素，连接线的终点
     * @param graphicInfoList 图形信息列表，描述连接线的原始路径
     * @return 返回优化后的连接线图形信息列表
     */
    protected static List<GraphicInfo> connectionPerfectionizer(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, BaseElement sourceElement, BaseElement targetElement, List<GraphicInfo> graphicInfoList) {
        // 获取源元素的图形信息
        GraphicInfo sourceGraphicInfo = bpmnModel.getGraphicInfo(sourceElement.getId());
        // 获取目标元素的图形信息
        GraphicInfo targetGraphicInfo = bpmnModel.getGraphicInfo(targetElement.getId());

        // 获取源元素的形状类型
        CustomDefaultProcessDiagramCanvas.SHAPE_TYPE sourceShapeType = getShapeType(sourceElement);
        // 获取目标元素的形状类型
        CustomDefaultProcessDiagramCanvas.SHAPE_TYPE targetShapeType = getShapeType(targetElement);
        // 调用流程图画布的优化方法，传入元素的形状类型和图形信息，以及原始路径，返回优化后的路径
        return processDiagramCanvas.connectionPerfectionizer(sourceShapeType, targetShapeType, sourceGraphicInfo, targetGraphicInfo, graphicInfoList);
    }

    /**
     * This method returns shape type of base element.<br>
     * Each element can be presented as rectangle, rhombus, or ellipse.
     *
     * @param baseElement
     * @return CustomDefaultProcessDiagramCanvas.SHAPE_TYPE
     */
    /**
     * 根据基础元素的类型确定形状类型
     * 该方法用于在流程图画布中确定不同流程元素（如任务、网关、事件）的图形表示
     *
     * @param baseElement 流程图中的基础元素，可以是任何类型的流程节点（如任务、网关、事件等）
     * @return 返回对应的形状类型，包括矩形、菱形、椭圆；如果无法确定元素类型，则不返回任何形状类型
     */
    protected static CustomDefaultProcessDiagramCanvas.SHAPE_TYPE getShapeType(BaseElement baseElement) {
        // 判断基础元素是否为任务、活动或文本注释，如果是，则返回矩形
        if (baseElement instanceof Task || baseElement instanceof Activity || baseElement instanceof TextAnnotation) {
            return CustomDefaultProcessDiagramCanvas.SHAPE_TYPE.Rectangle;
        } else if (baseElement instanceof Gateway) {
            // 判断基础元素是否为网关，如果是，则返回菱形
            return CustomDefaultProcessDiagramCanvas.SHAPE_TYPE.Rhombus;
        } else if (baseElement instanceof Event) {
            // 判断基础元素是否为事件，如果是，则返回椭圆
            return CustomDefaultProcessDiagramCanvas.SHAPE_TYPE.Ellipse;
        } else {
            // unknown source element, just do not correct coordinates
            // 对于未知的元素类型，不返回任何形状类型
            // 这里相当于一种默认处理机制，确保程序不会因为未知元素类型而异常
        }
        return null;
    }

    /**
     * 计算线的中心点
     * 该方法通过给定的图形信息列表，计算出线的中心点（中点）的坐标
     * 主要用于需要确定线段几何中心的场景
     *
     * @param graphicInfoList 包含线各个顶点的GraphicInfo列表
     * @return 返回线中心点的GraphicInfo对象
     */
    protected static GraphicInfo getLineCenter(List<GraphicInfo> graphicInfoList) {
        // 创建一个新的GraphicInfo对象来存储计算结果
        GraphicInfo gi = new GraphicInfo();

        // 初始化数组来存储列表中各点的x和y坐标
        int[] xPoints = new int[graphicInfoList.size()];
        int[] yPoints = new int[graphicInfoList.size()];

        // 初始化长度变量，用于计算线的总长度
        double length = 0;

        // 初始化数组，用于存储到每个点的累计长度
        double[] lengths = new double[graphicInfoList.size()];
        lengths[0] = 0;
        double m;

        // 遍历图形信息列表，计算各点间的距离并累计总长度
        for (int i = 1; i < graphicInfoList.size(); i++) {
            GraphicInfo graphicInfo = graphicInfoList.get(i);
            GraphicInfo previousGraphicInfo = graphicInfoList.get(i - 1);

            // 存储前一个点的坐标
            if (i == 1) {
                xPoints[0] = (int) previousGraphicInfo.getX();
                yPoints[0] = (int) previousGraphicInfo.getY();
            }

            // 存储当前点的坐标
            xPoints[i] = (int) graphicInfo.getX();
            yPoints[i] = (int) graphicInfo.getY();

            // 计算当前点与前一个点之间的距离，并累加到总长度
            length += Math.sqrt(
                Math.pow((int) graphicInfo.getX() - (int) previousGraphicInfo.getX(), 2) +
                    Math.pow((int) graphicInfo.getY() - (int) previousGraphicInfo.getY(), 2));
            // 记录到当前点的累计长度
            lengths[i] = length;
        }
        // 计算线的中点长度坐标
        m = length / 2;
        // 初始化两个指针p1和p2，用于定位中点所在的线段
        int p1 = 0;
        int p2 = 1;
        // 寻找中点所在的线段
        for (int i = 1; i < lengths.length; i++) {
            double len = lengths[i];
            // 更新p1和p2的值
            p1 = i - 1;
            p2 = i;
            // 如果找到中点所在的线段，跳出循环
            if (len > m) {
                break;
            }
        }

        // 获取中点所在线段的两个端点的GraphicInfo对象
        GraphicInfo graphicInfo1 = graphicInfoList.get(p1);
        GraphicInfo graphicInfo2 = graphicInfoList.get(p2);

        // 计算中点在x轴和y轴上的投影长度
        double AB = (int) graphicInfo2.getX() - (int) graphicInfo1.getX();
        double OA = (int) graphicInfo2.getY() - (int) graphicInfo1.getY();
        double OB = lengths[p2] - lengths[p1];
        double ob = m - lengths[p1];
        double ab = AB * ob / OB;
        double oa = OA * ob / OB;

        // 计算中点的坐标
        double mx = graphicInfo1.getX() + ab;
        double my = graphicInfo1.getY() + oa;

        // 设置并返回中点的GraphicInfo对象
        gi.setX(mx);
        gi.setY(my);
        return gi;
    }

    /**
     * 绘制流程图中的Artifact（注释或标记）
     * 本方法根据特定的Artifact类型，使用预定义的绘制指令来在流程图上绘制Artifact
     *
     * @param processDiagramCanvas 流程图画布，用于实际绘制操作
     * @param bpmnModel BPMN模型，提供模型相关的上下文信息
     * @param artifact 要绘制的Artifact对象，可以是文本注释、标记等
     */
    protected void drawArtifact(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact) {

        // 根据Artifact类型获取对应的绘制指令
        ArtifactDrawInstruction drawInstruction = artifactDrawInstructions.get(artifact.getClass());
        // 如果找到了对应的绘制指令，则调用其绘制方法来绘制Artifact
        if (drawInstruction != null) {
            drawInstruction.draw(processDiagramCanvas, bpmnModel, artifact);
        }
    }

    /**
     * 在流程图画布上绘制高亮区域
     *
     * @param processDiagramCanvas 流程图画布，用于绘制高亮
     * @param graphicInfo 图形信息，包含位置和尺寸信息
     */
    private static void drawHighLight(CustomDefaultProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) {
        // 调用画布的绘制高亮方法，传入整型的位置和尺寸参数
        processDiagramCanvas.drawHighLight((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight());
    }

    /**
     * 在流程图画布中绘制红色高亮区域
     *
     * 此方法用于在流程图中绘制红色高亮，以视觉上突出某个元素或区域
     * 它通过将GraphicInfo对象中的位置和尺寸信息转换为整数，然后调用
     * processDiagramCanvas对象的drawHighLightRed方法来实现绘制
     *
     * @param processDiagramCanvas 流程图绘制画布，用于绘制高亮的载体
     * @param graphicInfo 图形信息对象，包含需要绘制高亮的元素的位置和尺寸信息
     */
    private static void drawHighLightRed(CustomDefaultProcessDiagramCanvas processDiagramCanvas, GraphicInfo graphicInfo) {
        processDiagramCanvas.drawHighLightRed((int) graphicInfo.getX(), (int) graphicInfo.getY(), (int) graphicInfo.getWidth(), (int) graphicInfo.getHeight());

    }

    /**
     * 初始化流程图画布
     *
     * 此方法用于创建一个自定义的流程图画布对象，该对象用于后续绘制流程图
     * 它基于给定的BpmnModel以及一系列的格式化和类加载器设置进行初始化
     *
     * @param bpmnModel 流程模型，用于存储流程定义的信息，是绘制流程图的基础
     * @param imageType 图像类型，定义了生成的流程图的格式，如PNG、JPEG等
     * @param activityFontName 活动字体名称，用于在流程图中渲染活动名称的字体
     * @param labelFontName 标签字体名称，用于在流程图中渲染标签的字体
     * @param annotationFontName 注释字体名称，用于在流程图中渲染注释的字体
     * @param customClassLoader 自定义类加载器，用于加载绘制流程图所需的自定义资源或类
     * @return CustomDefaultProcessDiagramCanvas 返回一个初始化的自定义流程图画布对象，如果初始化失败则返回null
     */
    protected static CustomDefaultProcessDiagramCanvas initProcessDiagramCanvas(BpmnModel bpmnModel, String imageType,
                                                                                String activityFontName, String labelFontName, String annotationFontName, ClassLoader customClassLoader) {

        // We need to calculate maximum values to know how big the image will be in its entirety
        // 初始化最小X坐标值为Java中双精度浮点数的最大值
        double minX = Double.MAX_VALUE;
        // 初始化最大X坐标值为0
        double maxX = 0;
        // 初始化最小Y坐标值为Java中双精度浮点数的最大值
        double minY = Double.MAX_VALUE;
        // 初始化最大Y坐标值为0
        double maxY = 0;

        // 遍历BPMN模型中的所有池
        for (Pool pool : bpmnModel.getPools()) {
            // 获取当前池的图形信息
            GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(pool.getId());
            // 设置当前池的最小X坐标
            minX = graphicInfo.getX();
            // 设置当前池的最大X坐标，等于其X坐标加上宽度
            maxX = graphicInfo.getX() + graphicInfo.getWidth();
            // 设置当前池的最小Y坐标
            minY = graphicInfo.getY();
            // 设置当前池的最大Y坐标，等于其Y坐标加上高度
            maxY = graphicInfo.getY() + graphicInfo.getHeight();
        }

        // 根据给定的bpmnModel，收集所有的FlowNode
        List<FlowNode> flowNodes = gatherAllFlowNodes(bpmnModel);
        // 遍历所有收集到的FlowNode，以确定它们在图中的最大、最小坐标
        for (FlowNode flowNode : flowNodes) {

            // 获取当前FlowNode的图形信息，包括位置和尺寸
            GraphicInfo flowNodeGraphicInfo = bpmnModel.getGraphicInfo(flowNode.getId());

            // width
            // 计算最大宽度
            if (flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth() > maxX) {
                maxX = flowNodeGraphicInfo.getX() + flowNodeGraphicInfo.getWidth();
            }

            // 计算最小宽度
            if (flowNodeGraphicInfo.getX() < minX) {
                minX = flowNodeGraphicInfo.getX();
            }

            // height
            // 计算最大高度
            if (flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight() > maxY) {
                maxY = flowNodeGraphicInfo.getY() + flowNodeGraphicInfo.getHeight();
            }
            // 计算最小高度
            if (flowNodeGraphicInfo.getY() < minY) {
                minY = flowNodeGraphicInfo.getY();
            }


            // 遍历流程节点的所有流出序列流
            for (SequenceFlow sequenceFlow : flowNode.getOutgoingFlows()) {
                // 获取序列流的图形信息列表
                List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(sequenceFlow.getId());
                // 检查图形信息列表是否不为空
                if (graphicInfoList != null) {
                    // 遍历图形信息列表
                    for (GraphicInfo graphicInfo : graphicInfoList) {
                        // 更新最大X坐标
                        if (graphicInfo.getX() > maxX) {
                            maxX = graphicInfo.getX();
                        }
                        // 更新最小X坐标
                        if (graphicInfo.getX() < minX) {
                            minX = graphicInfo.getX();
                        }
                        // 更新最大Y坐标
                        if (graphicInfo.getY() > maxY) {
                            maxY = graphicInfo.getY();
                        }
                        // 更新最小Y坐标
                        if (graphicInfo.getY() < minY) {
                            minY = graphicInfo.getY();
                        }
                    }
                }
            }
        }

        // 根据给定的BPMN模型，收集所有的工件
        List<Artifact> artifacts = gatherAllArtifacts(bpmnModel);
        // 遍历每个工件以获取其图形信息
        for (Artifact artifact : artifacts) {
            // 获取当前工件的图形信息
            GraphicInfo artifactGraphicInfo = bpmnModel.getGraphicInfo(artifact.getId());

            // 如果当前工件的图形信息不为空，则更新最大、最小坐标
            if (artifactGraphicInfo != null) {
                // width
                // 更新最大和最小X坐标（宽度）
                if (artifactGraphicInfo.getX() + artifactGraphicInfo.getWidth() > maxX) {
                    maxX = artifactGraphicInfo.getX() + artifactGraphicInfo.getWidth();
                }
                if (artifactGraphicInfo.getX() < minX) {
                    minX = artifactGraphicInfo.getX();
                }
                // height
                // 更新最大和最小Y坐标（高度）
                if (artifactGraphicInfo.getY() + artifactGraphicInfo.getHeight() > maxY) {
                    maxY = artifactGraphicInfo.getY() + artifactGraphicInfo.getHeight();
                }
                if (artifactGraphicInfo.getY() < minY) {
                    minY = artifactGraphicInfo.getY();
                }
            }

            // 获取与工件相关的流程位置图形信息，并更新最大、最小坐标
            List<GraphicInfo> graphicInfoList = bpmnModel.getFlowLocationGraphicInfo(artifact.getId());
            if (graphicInfoList != null) {
                for (GraphicInfo graphicInfo : graphicInfoList) {
                    // width
                    // 更新最大和最小X坐标（宽度）
                    if (graphicInfo.getX() > maxX) {
                        maxX = graphicInfo.getX();
                    }
                    if (graphicInfo.getX() < minX) {
                        minX = graphicInfo.getX();
                    }
                    // height
                    // 更新最大和最小Y坐标（高度）
                    if (graphicInfo.getY() > maxY) {
                        maxY = graphicInfo.getY();
                    }
                    if (graphicInfo.getY() < minY) {
                        minY = graphicInfo.getY();
                    }
                }
            }
        }

        // 初始化泳道数量
        int nrOfLanes = 0;
        // 遍历模型中的所有过程
        for (Process process : bpmnModel.getProcesses()) {
            // 遍历过程中的所有泳道
            for (Lane l : process.getLanes()) {

                // 泳道数量加一
                nrOfLanes++;

                // 获取泳道的图形信息
                GraphicInfo graphicInfo = bpmnModel.getGraphicInfo(l.getId());
                // width
                // 比较并更新最大X坐标值
                if (graphicInfo.getX() + graphicInfo.getWidth() > maxX) {
                    maxX = graphicInfo.getX() + graphicInfo.getWidth();
                }
                // 比较并更新最小X坐标值
                if (graphicInfo.getX() < minX) {
                    minX = graphicInfo.getX();
                }
                // height
                // 比较并更新最大Y坐标值
                if (graphicInfo.getY() + graphicInfo.getHeight() > maxY) {
                    maxY = graphicInfo.getY() + graphicInfo.getHeight();
                }
                // 比较并更新最小Y坐标值
                if (graphicInfo.getY() < minY) {
                    minY = graphicInfo.getY();
                }
            }
        }

        // Special case, see https://activiti.atlassian.net/browse/ACT-1431
        // 当流程节点、泳池和泳道数量均为0时，将坐标原点设置为(0, 0)
        // 这表明在模型为空时，没有可视元素需要展示
        if (flowNodes.isEmpty() && bpmnModel.getPools().isEmpty() && nrOfLanes == 0) {
            // Nothing to show
            minX = 0;
            minY = 0;
        }

        // 创建自定义默认流程图画布对象
        // 此处通过指定画布的宽度、高度以及图像类型等参数，来初始化一个画布对象
        // 这对于流程图的绘制是初始且关键的一步
        return new CustomDefaultProcessDiagramCanvas((int) maxX + 10, (int) maxY + 10, (int) minX, (int) minY,
            imageType, activityFontName, labelFontName, annotationFontName, customClassLoader);
    }

    /**
     * 收集BPMN模型中的所有工件
     *
     * @param bpmnModel BPMN模型，包含了流程中的所有元素和工件
     * @return 返回一个包含所有工件的列表
     */
    protected static List<Artifact> gatherAllArtifacts(BpmnModel bpmnModel) {
        // 创建一个列表来存储所有工件
        List<Artifact> artifacts = new ArrayList<>();
        // 遍历BPMN模型中的所有流程
        for (Process process : bpmnModel.getProcesses()) {
            // 将当前流程中的所有工件添加到 artifacts 列表中
            artifacts.addAll(process.getArtifacts());
        }
        // 返回包含所有工件的列表
        return artifacts;
    }

    /**
     * 从指定的BpmnModel中收集所有FlowNode
     * 此方法是聚集流程节点的入口，它遍历BpmnModel中的所有流程，并递归地收集每个流程中的所有FlowNode
     *
     * @param bpmnModel 包含流程定义的BpmnModel，从中提取FlowNode
     * @return 包含所有FlowNode的列表
     */
    protected static List<FlowNode> gatherAllFlowNodes(BpmnModel bpmnModel) {
        List<FlowNode> flowNodes = new ArrayList<>();
        for (Process process : bpmnModel.getProcesses()) {
            flowNodes.addAll(gatherAllFlowNodes(process));
        }
        return flowNodes;
    }

    /**
     * 递归收集流程容器中的所有流程节点
     *
     * 此方法遍历一个流程元素容器，将其中所有的流程节点（FlowNode）收集到一个列表中返回流程节点是流程中
     * 的一个基本元素，包括任务、网关等这个方法不仅收集直接包含在顶层容器中的节点，还会递归进入嵌套的容器继续收集节点
     *
     * @param flowElementsContainer 流程元素容器，包含各种流程节点
     * @return 包含所有流程节点的列表
     */
    protected static List<FlowNode> gatherAllFlowNodes(FlowElementsContainer flowElementsContainer) {
        List<FlowNode> flowNodes = new ArrayList<>();
        for (FlowElement flowElement : flowElementsContainer.getFlowElements()) {
            if (flowElement instanceof FlowNode) {
                flowNodes.add((FlowNode) flowElement);
            }
            if (flowElement instanceof FlowElementsContainer) {
                flowNodes.addAll(gatherAllFlowNodes((FlowElementsContainer) flowElement));
            }
        }
        return flowNodes;
    }

    /**
     * 判断一个流程元素是否属于折叠的子流程
     *
     * 此方法用于确定在BPMN模型中，当前流程元素是否直接隶属于一个折叠的子流程
     * 如果是，返回true；如果不是，继续递归检查其父子流程是否属于折叠的子流程
     *
     * @param flowElement 流程元素，用于检查其是否属于折叠的子流程
     * @param model BpmnModel对象，提供了BPMN模型的信息
     * @return 如果流程元素属于折叠的子流程，则返回true；否则返回false
     */
    protected boolean isPartOfCollapsedSubProcess(FlowElement flowElement, BpmnModel model) {
        // 获取流程元素关联的子流程
        SubProcess subProcess = flowElement.getSubProcess();
        if (subProcess != null) {
            // 获取子流程的图形信息，包括是否展开的状态
            GraphicInfo graphicInfo = model.getGraphicInfo(subProcess.getId());
            if (graphicInfo != null && graphicInfo.getExpanded() != null && !graphicInfo.getExpanded()) {
                // 如果子流程的图形信息表明它被折叠，则返回true
                return true;
            }

            // 递归检查当前子流程是否属于一个折叠的子流程
            return isPartOfCollapsedSubProcess(subProcess, model);
        }

        // 如果当前流程元素不属于任何子流程，或者所属子流程没有折叠，则返回false
        return false;
    }

    /**
     * 获取活动绘制指令映射
     * 此方法返回一个映射，其中包含了可绘制活动元素的类型和对应的绘制指令
     * 该映射使得框架能够根据元素类型查询并获取相应的绘制指令，以在图形界面中正确绘制各个活动元素
     *
     * @return 返回一个映射，键为可绘制活动元素的类类型，值为对应的绘制指令对象
     */
    public Map<Class<? extends BaseElement>, ActivityDrawInstruction> getActivityDrawInstructions() {
        return activityDrawInstructions;
    }

    /**
     * 设置活动绘制指令集合
     *
     * @param activityDrawInstructions 活动绘制指令的映射集合，键为BaseElement的子类类型，值为对应的绘制指令
     * @see BaseElement
     * @see ActivityDrawInstruction
     */
    public void setActivityDrawInstructions(
        Map<Class<? extends BaseElement>, ActivityDrawInstruction> activityDrawInstructions) {
        this.activityDrawInstructions = activityDrawInstructions;
    }

    /**
     * 获取工件绘制指令映射
     *
     * @return 返回一个映射，该映射包含BaseElement子类与对应的工件绘制指令的映射关系
     */
    public Map<Class<? extends BaseElement>, ArtifactDrawInstruction> getArtifactDrawInstructions() {
        return artifactDrawInstructions;
    }

    /**
     * 设置工件的绘制指令
     *
     * 此方法允许用户根据工件的类型指定自定义的绘制指令这些指令用于在图形界面上渲染工件时提供具体的绘制细节
     * 通过使用不同的绘制指令，可以控制不同类型工件的外观表现，例如线条的样式、颜色等
     *
     * @param artifactDrawInstructions 一个映射表，键是工件类的类型（必须是BaseElement的子类），值是对应的绘制指令
     *                                 这个映射表定义了如何绘制每种类型的工件
     */
    public void setArtifactDrawInstructions(
        Map<Class<? extends BaseElement>, ArtifactDrawInstruction> artifactDrawInstructions) {
        this.artifactDrawInstructions = artifactDrawInstructions;
    }

    /**
     * 定义了一个受保护的接口ActivityDrawInstruction，用于绘制流程图中的活动
     * 该接口的作用是定义一个操作，该操作能够在一个自定义的流程图画布上绘制指定的流程节点
     * 该接口的实现将依赖于具体的流程节点类型和绘制逻辑
     */
    protected interface ActivityDrawInstruction {
        /**
         * 绘制流程节点的方法
         * 该方法接受一个流程图画布对象，一个BpmnModel模型对象，和一个FlowNode流程节点对象
         * 通过这些参数，实现该接口的方法可以在画布上绘制出指定的流程节点
         *
         * @param processDiagramCanvas 流程图画布对象，用于绘制流程图
         * @param bpmnModel BPMN模型对象，包含流程的所有信息
         * @param flowNode 流程节点对象，表示需要绘制的特定流程节点
         */
        void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, FlowNode flowNode);
    }

    /**
     * 定义了一个受保护的接口ArtifactDrawInstruction，用于绘制流程图中的注释元素
     * 该接口的作用是将特定的注释元素绘制到自定义的流程图画布上，基于给定的BPMN模型和注释元素信息
     *
     */
    protected interface ArtifactDrawInstruction {
        /**
         * 定义了一个受保护的接口ArtifactDrawInstruction，用于绘制流程图中的注释元素
         * 该接口的作用是将特定的注释元素绘制到自定义的流程图画布上，基于给定的BPMN模型和注释元素信息
         *
         * @param processDiagramCanvas 流程图画布，用于绘制BPMN模型
         * @param bpmnModel BPMN模型，包含流程的所有细节和结构信息
         * @param artifact 注释元素，表示要在流程图中绘制的具体注释对象
         */
        void draw(CustomDefaultProcessDiagramCanvas processDiagramCanvas, BpmnModel bpmnModel, Artifact artifact);
    }
}
